package com.xd.center.http.config;

import com.xd.center.http.annotation.EnableHttpClient;
import com.xd.center.http.annotation.HttpClient;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 注册到springBean中
 */
public class HttpClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware, BeanFactoryAware {

  private ResourceLoader resourceLoader;

  private Environment environment;

  private BeanFactory beanFactory;

  @Override
  public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    registerClients(metadata, registry);
  }


  public void registerClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    Set<String> basePackages = getBasePackages(metadata);

    ClassPathScanningCandidateComponentProvider scanner = getScanner();
    scanner.setResourceLoader(this.resourceLoader);
    scanner.addIncludeFilter(new AnnotationTypeFilter(HttpClient.class));
    for (String basePackage : basePackages) {
      Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage); //获取有HttpClient注解的类
      for (BeanDefinition candidateComponent : candidateComponents) {
        if (candidateComponent instanceof AnnotatedBeanDefinition) { // verify annotated class is an interface
          AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
          AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
          Assert.isTrue(annotationMetadata.isInterface(), "@HttpClient can only be specified on an interface");

          registerClient(registry, annotationMetadata);
        }
      }
    }
  }

  private void registerClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata) {
    String className = annotationMetadata.getClassName();
    char[] chars = className.substring(className.lastIndexOf(".") + 1).toCharArray();
    if (Character.isUpperCase(chars[0])) {
      chars[0] += 32;
    }
    String simpleName = String.valueOf(chars);
    BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(HttpClientFactoryBean.class);
    definition.addPropertyValue("type", className);
    definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

    AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
    beanDefinition.setPrimary(true);

    //注入时在FactoryBean中创建 代理
    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, simpleName);
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    //直接创建代理
    //((DefaultListableBeanFactory) this.beanFactory).registerSingleton(className, Feign.builder().contract(new FeignClientBean.ApiContract()).encoder(new GsonEncoder()).decoder(new GsonDecoder()).target(IndexClient.class, "http://a27.cindata.cn"));

  }

  protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
    Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableHttpClient.class.getCanonicalName());

    Set<String> basePackages = new HashSet<>();
    for (String pkg : (String[]) attributes.get("packages")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }

    if (basePackages.isEmpty()) { //获取当前引入注解类的包
      basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
    }
    return basePackages;
  }

  protected ClassPathScanningCandidateComponentProvider getScanner() {
    return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
      @Override
      protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        boolean isCandidate = false;
        if (beanDefinition.getMetadata().isIndependent()) {
          if (!beanDefinition.getMetadata().isAnnotation()) {
            isCandidate = true;
          }
        }
        return isCandidate;
      }
    };
  }

  @Override
  public void setEnvironment(Environment environment) {
    this.environment = environment;
  }

  @Override
  public void setResourceLoader(ResourceLoader resourceLoader) {
    this.resourceLoader = resourceLoader;
  }

  @Override
  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    this.beanFactory = beanFactory;
  }
}
